package com.yl.request.common.glaencrypt.sm2;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemReader;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.*;
import java.util.UUID;
import com.yl.request.common.glaencrypt.sm2.SM2Engine.Mode;

public class SM2Utils {
    public static final String ALGORITHM = "EC";
    public static final String SM2_DEFAULT_CURVE = "sm2p256v1";
    public static final int DEFAULT_KEY_SIZE = 1024;

    public SM2Utils() {
    }

    public static KeyPair generateKeyPair() throws Exception {
        return generateKeyPair(1024, "sm2p256v1", null);
    }

    public static KeyPair generateKeyPair(int keySize, String curve, byte[] seed) throws Exception {
        ECGenParameterSpec sm2p256v1 = new ECGenParameterSpec(curve);
        SecureRandom random = null == seed ? new SecureRandom() : new SecureRandom(seed);
        return generateKeyPair(keySize, random, sm2p256v1);
    }

    public static KeyPair generateKeyPair(int keySize, SecureRandom random, AlgorithmParameterSpec... params) throws Exception {
        KeyPairGenerator keyPairGen = getKeyPairGenerator("EC");
        if (keySize > 0) {
            if (keySize > 256) {
                keySize = 256;
            }

            if (null != random) {
                keyPairGen.initialize(keySize, random);
            } else {
                keyPairGen.initialize(keySize);
            }
        }

        if (params != null && params.length > 0) {
            AlgorithmParameterSpec[] var4 = params;
            int var5 = params.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                AlgorithmParameterSpec param = var4[var6];
                if (null != param) {
                    if (null != random) {
                        keyPairGen.initialize(param, random);
                    } else {
                        keyPairGen.initialize(param);
                    }
                }
            }
        }

        return keyPairGen.generateKeyPair();
    }

    public static KeyPairGenerator getKeyPairGenerator(String algorithm) throws Exception {
        Provider provider = Security.getProvider("BC");
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm, provider);
        return keyPairGen;
    }

    public static PrivateKey privateKeyFromPem(File pemFile) throws Exception {
        byte[] key = readKeyFromPem(pemFile);
        return generatePrivateKey(key);
    }

    public static PrivateKey generatePrivateKey(byte[] key) throws Exception {
        return getKeyFactory("EC").generatePrivate(new PKCS8EncodedKeySpec(key));
    }

    public static ECPrivateKeyParameters toParams(PrivateKey privateKey) throws InvalidKeyException {
        return (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(privateKey);
    }

    public static PublicKey publicKeyKeyFromPem(File pemFile) throws Exception {
        byte[] key = readKeyFromPem(pemFile);
        return generatePublicKey(key);
    }

    public static PublicKey generatePublicKey(byte[] key) throws Exception {
        return getKeyFactory("EC").generatePublic(new X509EncodedKeySpec(key));
    }

    public static ECPublicKeyParameters toParams(PublicKey publicKey) throws InvalidKeyException {
        return (ECPublicKeyParameters)ECUtil.generatePublicKeyParameter(publicKey);
    }

    public static KeyFactory getKeyFactory(String algorithm) throws Exception {
        Provider provider = Security.getProvider("BC");
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm, provider);
        return keyFactory;
    }

    public static byte[] readKeyFromPem(File pemFile) throws Exception {
        PemReader reader = new PemReader(new FileReader(pemFile));
        byte[] key = reader.readPemObject().getContent();
        return key;
    }

    public static byte[] readKeyFromPem(byte[] pemBytes) throws Exception {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(pemBytes);
        PemReader reader = new PemReader(new InputStreamReader(inputStream));
        byte[] key = reader.readPemObject().getContent();
        return key;
    }

    public static byte[] encrypt(PublicKey pubKey, byte[] srcData) throws Exception {
        ECPublicKeyParameters pubKeyParameters = toParams(pubKey);
        return encrypt(pubKeyParameters, srcData);
    }

    private static byte[] encrypt(ECPublicKeyParameters pubKeyParameters, byte[] srcData) throws InvalidCipherTextException {
        SM2Engine engine = new SM2Engine(new SM3Digest(), Mode.C1C3C2);
        ParametersWithRandom pwr = new ParametersWithRandom(pubKeyParameters, new SecureRandom());
        engine.init(true, pwr);
        return engine.processBlock(srcData, 0, srcData.length);
    }

    public static byte[] decrypt(PrivateKey priKey, byte[] sm2Cipher) throws Exception {
        return decrypt(priKey, sm2Cipher, false);
    }

    public static byte[] decrypt(PrivateKey priKey, byte[] sm2Cipher, boolean isAsn1) throws Exception {
        ECPrivateKeyParameters priKeyParameters = toParams(priKey);
        return decrypt(priKeyParameters, sm2Cipher, isAsn1);
    }

    private static byte[] decrypt(ECPrivateKeyParameters priKeyParameters, byte[] sm2Cipher, boolean isAsn1) throws Exception {
        SM2Engine engine = new SM2Engine(new SM3Digest(), Mode.C1C3C2);
        engine.init(false, priKeyParameters);
        if (isAsn1) {
            sm2Cipher = engine.asn1Toc1c3c2(sm2Cipher);
        }

        return engine.processBlock(sm2Cipher, 0, sm2Cipher.length);
    }

    public static void main(String[] args) throws Exception {
        String publicKey = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEqKd/Wj16j9szfY+qDWwxar6Wz2PT\nXRkh3Tbl8JSXmp68LiLRr67MnVeScdJtZxv3oFa6JQm7a+GZkheYyGx3vw==\n-----END PUBLIC KEY-----";
        String privateKeyStr = "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgRgtvaYhQMRZslywa\nwpgG+kLuivDwWrrAbnyk63TRxjagCgYIKoEcz1UBgi2hRANCAASop39aPXqP2zN9\nj6oNbDFqvpbPY9NdGSHdNuXwlJeanrwuItGvrsydV5Jx0m1nG/egVrolCbtr4ZmS\nF5jIbHe/\n-----END PRIVATE KEY-----";
        PublicKey pubKey = generatePublicKey(readKeyFromPem(publicKey.getBytes()));
        PrivateKey priKey = generatePrivateKey(readKeyFromPem(privateKeyStr.getBytes()));
        Base64 base64 = new Base64();
        byte[] sm2Cipher = Base64.decode("BBxo+ykT/phySbx0NQ1IrNrKkM8uU0f8AoO0pUY14Bnbbip5/SKMbOrANFx4GR/cCW5Ymj+0nlvIT8eJgi/7XZsYG68BKlwzuEn8u2qFS2XZpJDVQvAVQ5RByBsFtNRY2rr0FZ481AiP1eN3zppfB6w=");
        byte[] d0 = decrypt(priKey, sm2Cipher);
        System.err.println(d0.length);
        String e = UUID.randomUUID().toString();
        System.err.println(e);
        byte[] e0 = encrypt(pubKey, e.getBytes());
        System.err.println(e0.length);
        System.err.println(Base64.encode(e0));
        d0 = decrypt(priKey, e0);
        System.err.println(d0.length);
        System.err.println(new String(d0));
    }

    static {
        Provider provider = Security.getProvider("BC");
        if (provider == null) {
            Security.addProvider(new BouncyCastleProvider());
        }

    }
}